package router

import (
	"bytes"
	"container/list"
	"fmt"
	"time"

	"gitee.com/liukexing/isrn/model"

	manet "github.com/multiformats/go-multiaddr-net"
)

const (
	maxbuflen int   = 4096 * 1024
	maxtime   int64 = 60 * 10
)

const (
	noEncrypt_s = iota
	noEncrypt_e
	encrypt_s
	encrypt_e
)

const (
	unuse = iota
	using
	used
)

var (
	msgtemp chan *MessageInfo
)

type MessageInfo struct {
	Data    []byte
	State   int
	Encrypt int
	Conn    *ConnectInfo
}

type ConnectInfo struct {
	NodeId   string
	RCyprto  RemoteCyprto
	Conn     manet.Conn
	ReqChan  chan []byte
	ReqStat  bool
	ResChan  chan []byte
	ResStat  bool
	Active   bool
	LastTick time.Time
	Datatemp []byte
	Created  bool
}

func (c *ConnectInfo) SendMsg(data []byte) {
	if c.ResStat {
		c.ResChan <- data
	}
}

func getRemoteCyprto(isEncrypt bool, conn *ConnectInfo) *RemoteCyprto {
	if isEncrypt {
		return &conn.RCyprto
	}
	return nil

}

func getTransByte(data *model.Trans, isEncrypt bool, key *RemoteCyprto) ([]byte, error) {
	if key.Rpk.GetK() == nil {
		isEncrypt = false
	}
	pkbytes, err := key.Pk.Bytes()
	if err != nil {
		return nil, err
	}

	sendmsg, err := data.GetByte()
	if err != nil {
		return nil, err
	}
	msg := make([][]byte, 3)
	sep := []byte("")
	if isEncrypt {
		msgEn, err := key.Encrypt(sendmsg)
		if err != nil {
			return nil, err
		}
		msg[0] = pkbytes[headlen*encrypt_s : headlen*(encrypt_s+1)]
		msg[1] = msgEn
		msg[2] = pkbytes[headlen*encrypt_e : headlen*(encrypt_e+1)]
	} else {
		msg[0] = pkbytes[headlen*noEncrypt_s : headlen*(noEncrypt_s+1)]
		msg[1] = sendmsg
		msg[2] = pkbytes[headlen*noEncrypt_e : headlen*(noEncrypt_e+1)]
	}

	return bytes.Join(msg, sep), nil
}

func closeListen(conn *ConnectInfo) {
	conn.Active = false
	if conn.ReqStat {
		close(conn.ReqChan)
		conn.ReqStat = false
	}
	if conn.ResStat {
		close(conn.ResChan)
		conn.ResStat = false
	}
	if conn.Conn != nil {
		conn.Conn.Close()
	}
}

func receiverListen(conn *ConnectInfo) {
	defer closeListen(conn)
	for {
		data := make([]byte, 4096)
		c, err := conn.Conn.Read(data)
		if err != nil {
			conn.Active = false
			return
		}
		if c > 0 {
			conn.ReqChan <- data[0:c]
		}
	}
}

func sendListen(conn *ConnectInfo) {
	defer closeListen(conn)
	for msg := range conn.ResChan {
		conn.Conn.Write(msg)
	}
}

type KeySeq struct {
	Type int
	Pos  int
}

func listInsert(l *list.List, v *KeySeq) {
	if l.Len() == 0 {
		l.PushFront(v)
	} else {
		e := l.Front()
		for e != nil {
			if v.Pos < e.Value.(*KeySeq).Pos {
				l.InsertBefore(v, e)
				return
			}
			e = e.Next()
		}
		l.PushBack(v)
	}
	return
}

func searchKey(l *list.List, data []byte, keyType int) {
	key := headbytes[headlen*keyType : headlen*(keyType+1)]
	pos := bytes.Index(data, key)
	offset := 0
	for pos > -1 {
		v := &KeySeq{
			Type: keyType,
			Pos:  pos + offset,
		}
		listInsert(l, v)
		offset = pos + offset + headlen
		pos = bytes.Index(data[offset:], key)
	}
}

func dataDecode(data []byte, sender *ConnectInfo) {
	msg := make([][]byte, 2)
	msg[0] = sender.Datatemp
	msg[1] = data
	sender.Datatemp = bytes.Join(msg, []byte(""))

	l := list.New()
	searchKey(l, sender.Datatemp, noEncrypt_s)
	searchKey(l, sender.Datatemp, noEncrypt_e)
	searchKey(l, sender.Datatemp, encrypt_s)
	searchKey(l, sender.Datatemp, encrypt_e)
	if l.Len() < 2 {
		return
	}
	var keySeq_s, keySeq_e *list.Element = nil, nil
	e := l.Front()
	for e != nil {
		v := e.Value.(*KeySeq)
		if keySeq_s == nil {
			//find start key
			if v.Type == noEncrypt_s || v.Type == encrypt_s {
				keySeq_s = e
				keySeq_e = nil
			}
		} else {
			v_s := keySeq_s.Value.(*KeySeq)
			if v_s.Type == noEncrypt_s {
				if v.Type == noEncrypt_e {
					keySeq_e = e
				} else {
					if e.Next() != nil {
						v_n := e.Next().Value.(*KeySeq)
						if (v.Type == noEncrypt_s && v_n.Type == noEncrypt_e) || (v.Type == encrypt_s && v_n.Type == encrypt_e) {
							keySeq_s = e
							keySeq_e = e.Next()
						}
					}
				}

			}
			if v_s.Type == encrypt_s {
				if v.Type == encrypt_e {
					keySeq_e = e
				} else {
					if e.Next() != nil {
						v_n := e.Next().Value.(*KeySeq)
						if (v.Type == noEncrypt_s && v_n.Type == noEncrypt_e) || (v.Type == encrypt_s && v_n.Type == encrypt_e) {
							keySeq_s = e
							keySeq_e = e.Next()
						}
					}
				}
			}

		}
		if keySeq_s != nil && keySeq_e != nil {
			for keySeq_s.Prev() != nil && keySeq_s.Prev().Value.(*KeySeq).Type == keySeq_s.Value.(*KeySeq).Type {
				keySeq_s = keySeq_s.Prev()
			}
			for keySeq_e.Next() != nil && keySeq_e.Next().Value.(*KeySeq).Type == keySeq_e.Value.(*KeySeq).Type {
				keySeq_e = keySeq_e.Next()
			}
			e = keySeq_e

			v_s := keySeq_s.Value.(*KeySeq)
			v_e := keySeq_e.Value.(*KeySeq)
			msgtemp <- &MessageInfo{
				Data:    sender.Datatemp[v_s.Pos+headlen : v_e.Pos],
				State:   unuse,
				Encrypt: v_s.Type,
				Conn:    sender,
			}
			keySeq_s = nil
		}
		e = e.Next()
	}
	if keySeq_s != nil {
		v_s := keySeq_s.Value.(*KeySeq)
		sender.Datatemp = sender.Datatemp[v_s.Pos:]
	}
	if keySeq_e != nil {
		v_e := keySeq_e.Value.(*KeySeq)
		sender.Datatemp = sender.Datatemp[v_e.Pos+headlen:]
	}
	if len(sender.Datatemp) > maxbuflen {
		sender.Datatemp = []byte("")
	}
}

func messageLoop() {
	msgtemp = make(chan *MessageInfo, 1000)
	defer close(msgtemp)
	for info := range msgtemp {
		//		if info.Encrypt == encrypt_s {
		//			data, err := info.Conn.RCyprto.Decrypt(info.Data)
		//			if err != nil {
		//				continue
		//			}
		//			info.Data = data
		//		}
		go ApplyData(info)
	}
}

func ApplyData(info *MessageInfo) {
	sender := info.Conn
	if info.Encrypt == encrypt_s {
		data, err := sender.RCyprto.Decrypt(info.Data)
		if err != nil {
			return
		}
		info.Data = data
	}
	transdata := &model.Trans{}
	if data, err := transdata.GetData(info.Data); err != nil {
		info.State = used
		return
	} else {
		if time.Now().Unix()-data.PackInfo.TimeStamp > maxtime {
			fmt.Println("MessageInfo Pack TimeOut")
			return
		}
		isEncrypt := false
		if info.Encrypt == encrypt_s {
			isEncrypt = true
		}
		var e error
		switch data.PackType {
		case model.Boradcast:
			e = BoradcastController(data, isEncrypt, sender)
		case model.KeySwap:
			e = KeySwapController(data, isEncrypt, sender)
		case model.Tick:
			e = TickController(data, isEncrypt, sender)
		case model.Tran:
			e = TranController(data, isEncrypt, sender)
		}
		if e != nil {
			fmt.Println(e)
		}
		info.State = used
	}

}
